﻿using System;
using System.Collections.Generic;
using Bling.Util;
using Bling.DSL;
using Bling.Core;
using Bling.Graphics;
using Bling.Vecs;

namespace Bling.Ops {
  using Shaders;
  public static partial class DoubleOperators0<T> {
    public partial class Recip : MathOperator<Recip> {
      public override Func<string, string> Shader(Operation<T, T> op) {
        return null;
      }
    }

    public partial class Ceiling : MathOperator<Ceiling> {
      public override string ShaderCallName { get { return "ceil"; } }
    }
    public partial class Exp10 : MathOperator<Exp10> {
      public override Func<string, string> Shader(Operation<T, T> op) { return null; }
    }
    public partial class LogN : Math2Operator<LogN> {
      public override Func<string, string, string> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<T, T, T> op) { return null; }
    }
    public partial class LengthSquared : StaticCallOperator<T, double, LengthSquared> {
      public override Func<string, string> Shader(Operation<T, double> op) { return null; }
    }
    public partial class Square : MathOperator<Square> {
      public override Func<string, string> Shader(Operation<T, T> op) { return null; }
    }
  }
  public partial class Swizzle<K, S, T> : BaseOperatorX<S, T> {
    public override Func<string, string> Shader(Operation<S, T> op) {
      return Format;
    }
  }
  public partial class TupleConstruct<K, S, T, U> : Operator<S, T, U, TupleConstruct<K, S, T, U>>,
    ITupleConstruct<K, U, TupleConstruct<K, S, T, U>> {
    public override Func<string, string, string> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<S, T, U> op) {
      string prefix = null;
      if (typeof(K) == typeof(double) || typeof(K) == typeof(float)) prefix = "float";
      else if (typeof(K) == typeof(int)) prefix = "int";
      if (prefix == null) return base.Shader(txt, op);
      else return (a, b) => prefix + Arity<K, U>.ArityI.Count + "(" + a + ", " + b + ")";
    }

  }
  public static partial class DoubleOperators<T, B> {
    public partial class IsInfinity : DoubleOperator<IsInfinity> {
      public override string ShaderCallName { get { return "isinf"; } }
    }
    public partial class IsPositiveInfinity : DoubleOperator<IsPositiveInfinity> {
      public override Func<string, string> Shader(Operation<T, B> op) { return null; }
    }
    public partial class IsNegativeInfinity : DoubleOperator<IsNegativeInfinity> {
      public override Func<string, string> Shader(Operation<T, B> op) { return null; }
    }
  }
  public static partial class Trig1Operators<T> {
    public partial class SinCos : StaticCallOperator<double, T, SinCos> {
      public override Func<string, string> Shader(Operation<double, T> op) { return null; }
    }
  }
  public static partial class VecConvert<T, K, VEC> where VEC : Vec<K> {
    public abstract partial class Operator<A, B, CNT> : Ops.Operator<A, B, CNT> where CNT : Operator<A,B,CNT> {
      // no-op.
      public override Func<string, string> Shader(Operation<A, B> op) { return s => s; }
    }
  }

}
namespace Bling.Shaders {
  using Ops = Bling.Ops;
  using Brands = Bling.Core;
  using Bling.Core;

  public static partial class BaseShaderEval {
    public static string TypeNameFor(Type type, bool SupportsOthers, Func<Type, bool, string> SelfF) {
      if (type == typeof(uint)) return "uint";
      var arity = Vecs.Arity.Find(type);
      if (arity == null) throw new NotSupportedException();
      string prefix;
      if (arity.Count == 1) {
        var K = arity.TypeOfK;
        if (K == typeof(float) || K == typeof(double)) prefix = "float";
        else if (SupportsOthers) {
          if (K == typeof(int)) prefix = "int";
          else if (K == typeof(uint)) prefix = "uint";
          else if (K == typeof(bool)) prefix = "bool";
          else throw new NotSupportedException();
        } else prefix = "float";
      } else prefix = SelfF(arity.TypeOfK, SupportsOthers);

      if (arity is Matrices.IMatrixArity) {
        var matrix = (Matrices.IMatrixArity)arity;
        if (matrix.Height == 1) return prefix + matrix.Width.ToString();
        else if (matrix.Width == 1) return prefix + matrix.Height.ToString();
        return prefix + matrix.Height + "x" + matrix.Width;
      } else return prefix + (arity.Count == 1 ? "" : arity.Count.ToString());

    }


  }
  public abstract partial class BaseShaderEval<EVAL> : ScopedEval<EVAL>, Ops.IConditionEval2<EVAL>, ITableEval<EVAL>, ISampleEval<EVAL> where EVAL : Eval<EVAL> {
    public abstract Info<Vecs.Vec4<double>> Sample<DIM, PT>(Expr<Texture<DIM>> argA, Expr<PT> argB) where DIM : Matrices.DNum<DIM>;
    public override Info<K> Access<K, T, ARITY>(Info<T> info, int Idx) {
      var arity = Vecs.Arity<K, T>.ArityI;
      string select;
      var marity = arity as Matrices.IMatrixArity<K, T>;
      if (marity != null && marity.Width > 1 && marity.Height > 1) {
        // find the row first.
        var row = Idx / marity.Width;
        var column = Idx % marity.Width;
        select = "_m" + row + column;
      } else switch (Idx) {
        case 0: select = "x"; break;
        case 1: select = "y"; break;
        case 2: select = "z"; break;
        case 3: select = "w"; break;
        default: throw new NotSupportedException();
      }
      var info0 = ((MyInfo<T>)info).Value;
      // not in shader yet
      if (info0 == null) return Append(() => NewInfo<K>(null), null, info);
      return Append(() => NewInfo<K>(info0 + "." + select, info), null, info);
    }
    public override Info<T> Composite<K, T>(IComposite<K,T> Expr, params Info<K>[] infos) {
      var exprs = new Expr[infos.Length];
      for (int i = 0; i < infos.Length; i++) exprs[i] = Expr[i];

      return Prepare<T>(() => Append(() => {
        bool AnyValueNull = false;
        bool AllValueNull = true;
        for (int i = 0; i < infos.Length; i++) {
          var info = (MyInfo<K>)infos[i];
          AnyValueNull = AnyValueNull || info.Value == null;
          AllValueNull = AllValueNull && info.Value == null;
        }
        if (AnyValueNull) {
          AllValueNull.Assert();
          return NewInfo<T>(null, infos[0]);
        } 

        var str = TypeNameFor(typeof(T)) + "(";
        for (int i = 0; i < infos.Length; i++) {
          (((MyInfo<K>)infos[i]).Value != null).Assert();
          str = str + (i == 0 ? "" : ", ") + ((MyInfo<K>)infos[i]).Value;
        }
        return NewInfo<T>(str + ")", infos[0]);
      }, exprs, infos), exprs, infos);
    }
    public override Eval<EVAL>.Info<T> Constant<T>(Constant<T> constant) {
      var arity = Vecs.Arity.Find(typeof(T));
      if (arity != null) {
        Func<int, string> EToString = (idx) => {
          var e = arity.Access(constant.Value, idx);
          if (arity.TypeOfK == typeof(bool)) return ((bool)e) ? "1" : "0";
          else if (arity.TypeOfK == typeof(double)) return ((double)e).ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
          else return e.ToString();
        };
        if (arity.Count == 1) {
          return NewInfo<T>(EToString(0));
        }
        var str = TypeNameFor(arity.TypeOfT) + "(";
        for (int i = 0; i < arity.Count; i++) {
          str = str + (i == 0 ? "" : ", ") + EToString(i);
        }
        return NewInfo<T>(str + ")");
      } else if (typeof(T).GetGenericTypeDefinition() == typeof(Texture<>)) {
        var ret = NewInfo<T>(null);

        return ret; // pretend to be a uniform.
      }
      throw new NotImplementedException();
    }

  }
}

